from asyncio import gather
from datetime import datetime
from typing import Dict, List, Tuple

from common.basic_mongo import get_mongo_collection
from common.basic_rabbitmq import send_rabbitmq
from common.basic_redis import get_redis_conn, build_mg_redis_key
from device_status_mgm.device_status_constant import (DB_DEVICE_ALARM_DOCUMENT,
                                                      DB_DEVICE_EXCEPTION_STATUS_INTERVAL_DOCUMENT,
                                                      DB_DEVICE_HEARTBEAT_DOCUMENT,
                                                      MQ_DEVICE_STATUS_COMPUTE_PUSHING)
from common.utils import string_to_datetime, datetime_to_sting
from device_status_mgm.service.mark_interval import (
    build_mark_rapid_reason, single_mark_break_interval)
from mg_app_framework import get_logger, get_organization


async def device_heartbeat_report(
        *,
        upperpc_code: str,
        is_online: int,
):
    """
    设备在线心跳上报
    :param upperpc_code: 上位机编码(工序编码)
    :param is_online: 是否在线，1：在线，0：离线
    :return:
    """
    now_time = datetime.now()
    now_timestamp = datetime_to_sting(now_time)
    heartbeat_handler = get_mongo_collection(
        collection_name=DB_DEVICE_HEARTBEAT_DOCUMENT,
        database_name=f"idi_{get_organization()}_upper_pc",
    )
    await heartbeat_handler.insert_one(
        {"upperpc_code": upperpc_code, "is_online": str(is_online), "timestamp": now_time}
    )
    redis_handel = get_redis_conn()
    upper_pc_heartbeat_key = build_mg_redis_key(f"upper_pc_heartbeat:{upperpc_code}")
    await redis_handel.hmset(upper_pc_heartbeat_key, {
        "is_online": str(is_online),
        "timestamp": now_timestamp
    })


async def device_equip_status_report(
        *,
        upperpc_code: str,
        equip_code: str,
        equip_status: int,
        start_time: str,
        reason_code: str = None,
        user_code: str = None,
) -> Tuple[bool, str]:
    """
    设备状态上报
    :param upperpc_code: 上位机编码(工序编码)
    :param equip_code: 设备编码
    :param equip_status: 设备状态
    :param start_time:
    :param reason_code: 停机原因代码
    :param user_code:
    :return:
    """
    if reason_code:
        update_mark_reason = await build_mark_rapid_reason(reason_code, user_code)
        if not update_mark_reason:
            return False, f"停机代码:{reason_code},没有配置"
    else:
        update_mark_reason = {}
    await send_rabbitmq(
        key=MQ_DEVICE_STATUS_COMPUTE_PUSHING,
        data={
            "device_code": equip_code,
            "value": equip_status,
            "user_code": user_code,
            "timestamp": start_time,
            "exception_info": [],
            "update_mark_reason": update_mark_reason,
        },
    )
    return True, ""


async def device_equip_stop_reason_report(
        *,
        upperpc_code: str,
        equip_code: str,
        start_time: str,
        reason_code: str,
        user_code: str = None,
) -> Tuple[bool, str]:
    """
    设备停机原因上报
    :param upperpc_code: 上位机编码(工序编码)
    :param equip_code: 设备编码
    :param start_time:
    :param reason_code:
    :param user_code:
    :return:
    """
    dt_start_time = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
    device_interval_handler = get_mongo_collection(
        collection_name=DB_DEVICE_EXCEPTION_STATUS_INTERVAL_DOCUMENT
    )
    recently_interval = await device_interval_handler.find_one(
        {"device_code": equip_code, "start_time": {"$lte": dt_start_time}},
        sort=[("start_time", -1)],
    )
    if not recently_interval:
        get_logger().error(f"设备:{equip_code}, 时间段:{start_time}, 没有时间片数据, 添加标记异常!")
        return False, f"设备:{equip_code}, 时间段:{start_time}, 没有时间片数据, 添加标记异常!"

    update_mark_reason = await build_mark_rapid_reason(reason_code, user_code)
    if not update_mark_reason:
        return False, f"停机代码:{reason_code},没有配置"
    await single_mark_break_interval(
        device_code=equip_code,
        start_time=datetime_to_sting(recently_interval["start_time"]),
        update_mark_reason=update_mark_reason,
    )
    return True, ""


async def device_equip_alarm_report(
        *, upperpc_code: str, equip_code: str, user_code: str = None, alarm_list: List[Dict] = None
):
    """
    设备报警上报
    :param upperpc_code:
    :param equip_code:
    :param user_code:
    :param alarm_list:
        alarm_status: int, 1：发生报警; 0：解除报警
        start_time: str, 报警时间/解除时间
        alarm_code: str, 报警代码
    :return:
    """
    alarm_handler = get_mongo_collection(
        collection_name=DB_DEVICE_ALARM_DOCUMENT,
        database_name=f"idi_{get_organization()}_upper_pc",
    )
    all_tasks = []

    for alarm_info in alarm_list:
        alarm_status = alarm_info["alarm_status"]
        timestamp = string_to_datetime(alarm_info["start_time"])
        alarm_code = alarm_info["alarm_code"]
        all_tasks.append(
            alarm_handler.find_one_and_update(
                {
                    "device_code": equip_code,
                    "alarm_code": alarm_code,
                    "timestamp": timestamp,
                },
                {
                    "$set": {
                        "upperpc_code": upperpc_code,
                        "user_code": user_code,
                        "alarm_status": alarm_status,
                    }
                },
                upsert=True,
            )
        )
    await gather(*all_tasks)
